Skip to content

Improve "Name is not defined" errors with fuzzy matching#20693

Open
KevinRK29 wants to merge 5 commits intopython:masterfrom
KevinRK29:add-fuzzy-matching
Open

Improve "Name is not defined" errors with fuzzy matching#20693
KevinRK29 wants to merge 5 commits intopython:masterfrom
KevinRK29:add-fuzzy-matching

Conversation

@KevinRK29
Copy link
Collaborator

This PR collects all visible names in the current scope and uses fuzzy matching to provide suggestions in the error messages

@KevinRK29 KevinRK29 marked this pull request as draft January 30, 2026 06:13
@KevinRK29 KevinRK29 requested a review from JukkaL January 30, 2026 06:14
@github-actions

This comment has been minimized.

@github-actions
Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

manticore (https://github.com/trailofbits/manticore)
- tests/auto_generators/make_dump.py:390: error: Name "xrange" is not defined  [name-defined]
+ tests/auto_generators/make_dump.py:390: error: Name "xrange" is not defined; did you mean "range"?  [name-defined]

apprise (https://github.com/caronc/apprise)
- apprise/plugins/irc/client.py:115: error: Name "Deque" is not defined  [name-defined]
+ apprise/plugins/irc/client.py:115: error: Name "Deque" is not defined; did you mean "deque"?  [name-defined]

AutoSplit (https://github.com/Toufool/AutoSplit)
- src/utils.py:178:20: error: Name "FilterGraph" is not defined  [name-defined]
+ src/utils.py:178:20: error: Name "FilterGraph" is not defined; did you mean "filter_graph"?  [name-defined]
- src/utils.py:184:12: error: Name "COMError" is not defined  [name-defined]
+ src/utils.py:184:12: error: Name "COMError" is not defined; did you mean "IOError" or "OSError"?  [name-defined]

@KevinRK29 KevinRK29 marked this pull request as ready for review February 20, 2026 16:21
@@ -608,7 +608,7 @@ def f(x: _T) -> None: pass
s: FrozenSet
[out]
_program.py:2: error: Name "_T" is not defined
_program.py:3: error: Name "FrozenSet" is not defined
_program.py:3: error: Name "FrozenSet" is not defined; did you mean "frozenset"?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an explicit test in some check-*.test file that ensures that misspellings of builtins are detected?


y = MyClas() # E: Name "MyClas" is not defined; did you mean "MyClass"? [name-defined]

unknown_xyz # E: Name "unknown_xyz" is not defined [name-defined]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideas for additional tests:

  • Test a misspelling of a local variable.
  • Test a mispelling of a top-level definition within a function (misspelling targeting an outer namespace).
  • Test a misspelling of a class variable within class body.
  • Test a misspelling of name that is available through from import name.

and not (name.startswith("__") and name.endswith("__"))
and f"builtins.{name}" not in SUGGESTED_TEST_FIXTURES
):
alternatives = self._get_names_in_scope()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is pretty expensive, we shouldn't do the analysis if the error will just be ignored. This way we'd only slow things down when there is a user-visible error. In some codebases, there could be thousands of ignored errors, and this could generate non-trivial amounts of extra work.

My suggestion would be to refactor is_ignored_error in mypy/errors.py so that most of the checking (but not the blocker check) is available through is_ignored_error_code(line, code, ignores), for example. Then you can check if codes.NAME_DEFINED is ignored here, and skip the expensive logic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, it would be fine to skip the logic if there is any ignore comment on the line, if that's easier to implement.

names.update(table.keys())

if self.type is not None:
names.update(self.type.names.keys())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class namespace should only be included if we are not within a method (i.e. directly within class body only).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants